package com.pair.micro.fee.modules.charge.service.impl;


import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.pair.micro.common.constant.Constant;
import com.pair.micro.common.constant.MicroBusinessError;
import com.pair.micro.common.exception.MicroBusinessException;
import com.pair.micro.common.mybatis.service.impl.BaseServiceImpl;
import com.pair.micro.common.utils.DefinitionCode;
import com.pair.micro.common.utils.PageResult;
import com.pair.micro.fee.modules.charge.dao.ChargeDetailDao;
import com.pair.micro.fee.modules.charge.entity.*;
import com.pair.micro.fee.modules.charge.query.ChargeDetailQuery;
import com.pair.micro.fee.modules.charge.service.ChargeDetailService;
import com.pair.micro.fee.modules.charge.service.ChargeProjectService;
import com.pair.micro.fee.modules.charge.service.ChargeReceiveDetailService;
import com.pair.micro.fee.modules.charge.service.ChargeReceiveService;
import com.pair.micro.fee.modules.charge.vo.ChargeDetailVO;
import com.pair.micro.fee.modules.charge.vo.ChargeProjectVO;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class ChargeDetailServiceImpl extends BaseServiceImpl<ChargeDetailDao, ChargeDetailEntity> implements ChargeDetailService {
    private final DefinitionCode definitionCode;

    private final ChargeProjectService chargeProjectService;
    private final ChargeReceiveService chargeReceiveService;

    private final ChargeReceiveDetailService chargeReceiveDetailService;
    @Override
    public PageResult<ChargeDetailVO> page(ChargeDetailQuery query) {
        return null;
    }

    @Override
    public void save(List<RoomOwnerEntity> list, ChargeDetailVO vo) {

    }

    @Override
    public void update(ChargeDetailVO vo) {

    }

    @Override
    public void delete(List<Long> idList) {

    }

    @Override
    public void charge(ChargeDetailVO vo) {
        ChargeProjectEntity chargeProject = new ChargeProjectEntity();
        ChargeDetailEntity chargeDetail = new ChargeDetailEntity();
        String detailId = String.valueOf(definitionCode.nextId(DefinitionCode.CODE_PREFIX_CHARGE_DETAIL));
        String projectId = vo.getId();
        Map<String, Object> params = new HashMap<>();
        params.put("projectId",projectId);
        ChargeProjectEntity projectEntity = chargeProjectService.getListById(params);
        String chargeFormula = projectEntity.getChargeFormula();
        BigDecimal unitPrice = projectEntity.getUnitPrice();
        BigDecimal area = new BigDecimal(String.valueOf(projectEntity.getArea()));
        BigDecimal additionalFee = projectEntity.getAdditionalFee();
        String chargeMark = projectEntity.getChargeMark();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM"); // 定义格式化模式为"yyyy-MM"
        Date startTime = vo.getStartTime();//收费项 开始时间
        Date endTime = projectEntity.getConfigEndTime(); //收费设置 结束时间
        BigDecimal revAmount = projectEntity.getRevAmount(); // 收费项应收金额
        BigDecimal payFeeAmount;
        if(chargeFormula.equals(Constant.UNIT_PRICE_AREA)){ //如果公式为单价乘以面积加上附加费用 计算出月费用
            payFeeAmount = unitPrice.multiply(area).add(additionalFee).setScale(2, BigDecimal.ROUND_HALF_EVEN);
        }else if(chargeFormula.equals(Constant.FIXED_COSTS)){ //固定费用
            payFeeAmount = additionalFee.setScale(2, BigDecimal.ROUND_HALF_EVEN);
        }else{
            throw new MicroBusinessException(MicroBusinessError.BIS_20010,"暂时不支持该类公式");
        }
        BigDecimal projectReceiveAmount = vo.getReceiveAmount(); //应收费用
        BigDecimal projectPaidAmount = vo.getPaidAmount();//实收费用
        //查出该收费项目下的所有账单
        LambdaQueryWrapper<ChargeReceiveEntity> receive = new LambdaQueryWrapper<>();
        receive.eq(ChargeReceiveEntity::getProjectId,projectId).eq(ChargeReceiveEntity::getState,0).eq(ChargeReceiveEntity::getStatus,0);
        List<ChargeReceiveEntity> receiveVO = chargeReceiveService.list(receive);
//      List<ChargeReceiveVO> receiveVO = vo.getReceiveVO();//账单集合
        List<ChargeReceiveDetailEntity> receiveDetailList = new ArrayList<>(); //收费详情明细集合
        Date startDate = new Date();
        if(chargeMark.equals(Constant.CYCLE_FEE)&&receiveVO.size()>0){ //周期性费用
            if(projectReceiveAmount.compareTo(projectPaidAmount)!=0){ //应收金额和实收金额不相等  手动输入金额
                BigDecimal receiveFirstAmount = receiveVO.get(0).getReceiveAmount(); //第一条账单应收费用
                //应收金额和实收金额不相等时，收费存在三种情况：1、实收费用大于账单第一个月的费用 2、实收费用等于第一个月的费用(选择多个账单，最后输入了为第一个月的费用) 3、实收费用小于第一个月的费用
                if(projectPaidAmount.compareTo(receiveFirstAmount)>-1){ //实收费用大于等于账单第一个月的费用 两种情况：1、剩余费用为整月 2、剩余费用不为整月

                    BigDecimal remainAmount = projectPaidAmount.subtract(receiveFirstAmount); //减掉第一个月 剩下的费用
                    BigDecimal remainder = remainAmount.divideAndRemainder(payFeeAmount)[1].setScale(2, BigDecimal.ROUND_HALF_UP); //数组下标0 是商值  下标为1是取余的值
                    BigDecimal m = remainAmount.divideAndRemainder(payFeeAmount)[0].setScale(2, BigDecimal.ROUND_HALF_UP); //实收费用除以月费用

                    LocalDateTime startDay = LocalDateTime.ofInstant(receiveVO.get(0).getStartDate().toInstant(), ZoneId.systemDefault()); //第一个月日期
                    LocalDateTime startFirstTime = LocalDateTime.from(startDay.with(TemporalAdjusters.firstDayOfMonth())).plusMonths(m.longValue()+1);//下次缴费的账单开始时间和收费项开始时间,因为大于第一个月所以加1
                    LocalDateTime startLastTime = LocalDateTime.from(startDay.with(TemporalAdjusters.lastDayOfMonth())).plusMonths(m.longValue()+1); //下次缴费的账单开始时间月份的最后一天


                    String period = startFirstTime.format(formatter); //下次缴费的账单开始时间的 费用区间

                    if (remainder.compareTo(BigDecimal.ZERO) == 0) { //费用为第一个月份费用或者 为整月费用
                        LocalDateTime projectEndTime = LocalDateTime.ofInstant( vo.getEndTime().toInstant(), ZoneId.systemDefault()); //收费项截止时间
                        if(startFirstTime.compareTo(projectEndTime)==1){ //说明当期收费结束了
                            chargeProject.setChargeState(Constant.CHARGE_THIS_END);
                        }

                        startDate = toDate(startFirstTime);
                        LambdaQueryWrapper<ChargeReceiveEntity> receiveWrapper = new LambdaQueryWrapper<>();
                        //账单状态为收费中 并且账单应收区间小于下一次收费的应收区间 更新已收费状态
//                        receiveWrapper.lt(ChargeReceiveEntity::getPeriod,period).eq(ChargeReceiveEntity::getState,0).eq(ChargeReceiveEntity::getStatus,0);
//                      List<ChargeReceiveEntity> receiveEntityList = chargeReceiveService.list(receiveWrapper); //查出数据后保存，最后批量添加账单明细中
                        List<ChargeReceiveEntity> receiveEntityList = receiveVO.stream().filter(f ->  DateUtil.parse(f.getPeriod()).isBefore(DateUtil.parse(period)) ).collect(Collectors.toList());
                        receiveDetailList = receiveEntityList.stream().map(item -> {
                            String id = String.valueOf(definitionCode.nextId(DefinitionCode.CODE_PREFIX_CHARGE_RECEIVE_DETAIL));
                            ChargeReceiveDetailEntity receiveDetail = new ChargeReceiveDetailEntity();
                            ChargeReceiveEntity receiveEntity = new ChargeReceiveEntity();
                            receiveDetail.setId(id);
                            receiveDetail.setDetailId(detailId);
                            receiveDetail.setReceiveId(item.getId());
                            receiveDetail.setPeriod(item.getPeriod());
                            receiveDetail.setReceiveOldDate(item.getStartDate());
                            receiveDetail.setReceiveAmount(item.getReceiveAmount());
                            receiveDetail.setRevAmount(new BigDecimal(0));
                            receiveDetail.setPaidAmount(item.getReceiveAmount());
                            receiveDetail.setThisAmount(item.getReceiveAmount());

                            //封装 账单表
                            receiveEntity.setId(item.getId());
                            receiveEntity.setState(1);
                            receiveEntity.setRevAmount(new BigDecimal(0));
                            receiveEntity.setPaidAmount(item.getReceiveAmount());
                            chargeReceiveService.updateById(receiveEntity);
                            return receiveDetail;
                        }).collect(Collectors.toList());

                    } else { //费用不为整月
                        int lastDay = startLastTime.getDayOfMonth(); //取出最后一天，当月天数  例如31 30
                        BigDecimal balance = remainder.subtract(payFeeAmount.multiply(m)); //剩余费用
                        //计算出剩余钱 是多少天的
                        int day = (int)Math.floor(balance.divide(payFeeAmount.divide(new BigDecimal(lastDay), 2, BigDecimal.ROUND_HALF_UP), 2, BigDecimal.ROUND_HALF_UP).doubleValue());
                        if(day==lastDay){ //这里是四舍五入误差问题  例如 每月费用为40 当月31天  缴费39.99 那么day值就是31 那么开始时间就是当月最后一天
                            startDate = toDate(startLastTime);
                        }else{
                            startDate = toDate(startFirstTime.plusDays(day));
                        }
                        LocalDateTime projectEndTime = LocalDateTime.ofInstant( vo.getEndTime().toInstant(), ZoneId.systemDefault()); //收费项截止时间
                        if(startFirstTime.compareTo(projectEndTime)==1){ //说明当期收费结束了
                            chargeProject.setChargeState(Constant.CHARGE_THIS_END);
                        }
                        LambdaQueryWrapper<ChargeReceiveEntity> receiveWrapper = new LambdaQueryWrapper<>();
                        //账单状态为收费中 并且账单应收区间小于等于下一次收费的应收区间 查出数据
//                       receiveWrapper.le(ChargeReceiveEntity::getPeriod,period).eq(ChargeReceiveEntity::getState,0).eq(ChargeReceiveEntity::getStatus,0);
//                       List<ChargeReceiveEntity> receiveEntityList = chargeReceiveService.list(receiveWrapper); //查出数据后保存，最后批量添加账单明细中

                        List<ChargeReceiveEntity> receiveEntityList = receiveVO.stream().filter(f ->  DateUtil.parse(f.getPeriod()).isBeforeOrEquals(DateUtil.parse(period)) ).collect(Collectors.toList());
                        Date finalStartDate = startDate;
                        receiveDetailList = receiveEntityList.stream().map(item -> {
                            ChargeReceiveDetailEntity receiveDetail = new ChargeReceiveDetailEntity();
                            ChargeReceiveEntity receiveEntity = new ChargeReceiveEntity();
                            String id = String.valueOf(definitionCode.nextId(DefinitionCode.CODE_PREFIX_CHARGE_RECEIVE_DETAIL));
                            receiveDetail.setId(id);
                            receiveDetail.setDetailId(detailId);
                            receiveDetail.setReceiveId(item.getId());
                            receiveDetail.setPeriod(item.getPeriod());
                            if(period.equals(item.getPeriod())){ // 判断,如果本次收费账单最后一个月的应收区间   实收费用大于一个月且不为整月
                                //封装 账单明细表
                                receiveDetail.setReceiveOldDate(finalStartDate); //最后一个月的到期时间
                                receiveDetail.setReceiveAmount(item.getReceiveAmount());
                                receiveDetail.setRevAmount(item.getRevAmount().subtract(balance)); //欠款金额减去本次收费账单最后一个月剩余费用
                                receiveDetail.setPaidAmount(item.getPaidAmount().add(balance));//实收金额加上本次收费账单最后一个月剩余费用
                                //封装 账单表
                                receiveEntity.setId(item.getId());
                                receiveEntity.setDebtStartDate(finalStartDate);
                                receiveEntity.setRevAmount(item.getRevAmount().subtract(balance));
                                receiveEntity.setPaidAmount(item.getPaidAmount().add(balance));
                            }else {
                                //封装 账单明细表
                                receiveDetail.setReceiveOldDate(item.getStartDate());
                                receiveDetail.setReceiveAmount(item.getReceiveAmount());
                                receiveDetail.setRevAmount(new BigDecimal(0));
                                receiveDetail.setPaidAmount(item.getReceiveAmount());
                                receiveDetail.setThisAmount(item.getReceiveAmount());
                                //封装 账单表
                                receiveEntity.setId(item.getId());
                                receiveEntity.setStartDate(item.getStartDate());
                                receiveEntity.setRevAmount(new BigDecimal(0));
                                receiveEntity.setPaidAmount(item.getReceiveAmount());
                                receiveEntity.setState(1);
                            }
                            //修改 账单表
                            chargeReceiveService.updateById(receiveEntity);
                            return receiveDetail;
                        }).collect(Collectors.toList());
                    }
                }else{ //实收费用小于账单第一个月的费用
                    ChargeReceiveEntity receiveEntityFirst = receiveVO.get(0);
                    String id = String.valueOf(definitionCode.nextId(DefinitionCode.CODE_PREFIX_CHARGE_RECEIVE_DETAIL));
                    LocalDateTime startDay = LocalDateTime.ofInstant(receiveVO.get(0).getStartDate().toInstant(), ZoneId.systemDefault()); //第一个月开始日期
                    LocalDateTime startLastTime = LocalDateTime.from(startDay.with(TemporalAdjusters.lastDayOfMonth()));
                    int lastDay = startLastTime.getDayOfMonth(); //取出最后一天，当月天数  例如31 30
                    int day = (int)Math.floor(projectPaidAmount.divide(payFeeAmount.divide(new BigDecimal(lastDay), 2, BigDecimal.ROUND_HALF_UP), 2, BigDecimal.ROUND_HALF_UP).doubleValue());
                    if(day==lastDay){ //这里是四舍五入误差问题  例如 每月费用为40 当月31天  缴费39.99 那么day值就是31 那么开始时间就是当月最后一天
                        startDate = toDate(startLastTime);
                    }else{
                        startDate = toDate(startDay.plusDays(day));
                    }
//                    vo.setStartTime(startDate);
                    ChargeReceiveDetailEntity receiveDetail = new ChargeReceiveDetailEntity();
                    receiveDetail.setId(id);
                    receiveDetail.setReceiveId(receiveEntityFirst.getId());
                    receiveDetail.setDetailId(detailId);
                    receiveDetail.setPeriod(receiveEntityFirst.getPeriod());
                    receiveDetail.setReceiveOldDate(startDate);
                    receiveDetail.setReceiveAmount(receiveEntityFirst.getReceiveAmount());
                    receiveDetail.setRevAmount(receiveEntityFirst.getRevAmount().subtract(projectPaidAmount));
                    receiveDetail.setPaidAmount(receiveEntityFirst.getPaidAmount().add(projectPaidAmount));
                    receiveDetailList.add(receiveDetail);

                    //修改 账单表
                    ChargeReceiveEntity receiveEntity = new ChargeReceiveEntity();
                    receiveEntity.setId(receiveEntityFirst.getId());
                    receiveEntity.setStartDate(startDate);
                    receiveEntity.setRevAmount(receiveEntityFirst.getRevAmount().subtract(projectPaidAmount));
                    receiveEntity.setPaidAmount(receiveEntityFirst.getPaidAmount().add(projectPaidAmount));
                    chargeReceiveService.updateById(receiveEntity);
                }
            }
            //开始时间减去一天为详情的缴费结束时间
            LocalDateTime startDay = LocalDateTime.ofInstant(startDate.toInstant(), ZoneId.systemDefault()).minusDays(1);
            chargeDetail.setStartTime(startTime);
            chargeDetail.setEndTime(toDate(startDay));
        }else if(chargeMark.equals(Constant.ONCE_FEE)||chargeMark.equals(Constant.OFTEN_FEE)){ //一次性费用费用或常用性费用
            String id = String.valueOf(definitionCode.nextId(DefinitionCode.CODE_PREFIX_CHARGE_RECEIVE_DETAIL));
            String period = DateUtil.format(startTime, formatter);
            ChargeReceiveDetailEntity receiveDetail = new ChargeReceiveDetailEntity();
            receiveDetail.setId(id);
            receiveDetail.setReceiveId(Constant.ONCE_FEE); //一次性费用给字段一个默认值
            receiveDetail.setDetailId(detailId);
            receiveDetail.setPeriod(period);
            receiveDetail.setReceiveOldDate(startTime);
            receiveDetail.setReceiveAmount(projectPaidAmount);
            receiveDetail.setRevAmount(new BigDecimal(0));
            receiveDetail.setPaidAmount(projectPaidAmount);
            receiveDetailList.add(receiveDetail);
            if(chargeMark.equals(Constant.CYCLE_FEE)&&projectEntity.getCasual()==1){ //为常用性临时收费
                Date startCasualTime = vo.getStartCasualTime();
                Date endCasualTime = vo.getEndCasualTime();
                //临时时间加一天为收费项 下次开始时间
                LocalDateTime startDay = LocalDateTime.ofInstant(endCasualTime.toInstant(), ZoneId.systemDefault()).plusDays(1);
                startDate = toDate(startDay);
                chargeDetail.setStartTime(startCasualTime);
                chargeDetail.setEndTime(endCasualTime);
            }else {
                chargeDetail.setStartTime(startTime);
                chargeDetail.setEndTime(endTime);
                startDate=endTime;
            }
        }else {
            throw new MicroBusinessException(MicroBusinessError.BIS_20010,"暂时不支持该收费类型");
        }

        //收费项目修改
        chargeProject.setId(projectId);
        chargeProject.setStartTime(startDate);
        BigDecimal revAmountSub = revAmount.subtract(projectPaidAmount);
        chargeProject.setRevAmount(revAmountSub);
        chargeProjectService.updateById(chargeProject);

        //报错收费详情明细
        chargeReceiveDetailService.saveBatch(receiveDetailList);

        //修改退费删除按钮
        LambdaQueryWrapper<ChargeDetailEntity> wrapper = new LambdaQueryWrapper();
        wrapper.eq(ChargeDetailEntity::getProjectId,projectId);
        chargeDetail.setBtn(0);
        baseMapper.update(chargeDetail, wrapper);

        //添加
        chargeDetail.setId(detailId);
        chargeDetail.setProjectId(projectId);
        chargeDetail.setPaidAmount(projectPaidAmount);
        chargeDetail.setState(20243001);
        chargeDetail.setBtn(1);
        chargeDetail.setReceiptNumber(vo.getReceiptNumber());
        chargeDetail.setChargeMethod(vo.getChargeMethod());
        baseMapper.insert(chargeDetail);
    }
    public static Date toDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }
}
